
#include "ModuleFactory.hpp"
#include "FiFoBuffer.hpp"

#include "Log.hpp"

#include "Statistics.hpp"


using namespace Bus ;
using namespace FiFo ;
using namespace SystemC ;






char const*const	kStateIdle				=	"Idle" ;
char const*const	kStateWaitingForBus		=	"Waiting for Bus" ;
char const*const	kStateTransferring		=	"Transferring" ;
char const*const	kStateTransferFailed	=	"Transfer Failed" ;








struct	WritableBusMappedFiFoSettings : public Settings
{
	std::string	busMasterName ;
	std::string	fifoName ;
	unsigned	targetAddress ;
	unsigned	burstSize ;
	unsigned	capacity ;
	SCInterval	fifoDelay ;
	
	WritableBusMappedFiFoSettings ( std::string const & name )
	:	Settings ( name )
	,	busMasterName()
	,	fifoName()
	,	targetAddress(0)
	,	burstSize(0)
	,	capacity(0)
	,	fifoDelay(0,kSCIntervalSeconds)
		{
		add ( "busMasterName" , busMasterName ) ;
		add ( "fifoName" , fifoName ) ;
		add ( "targetAddress" , targetAddress ) ;
		add ( "burstLength" , burstSize ) ;
		add ( "capacity" , capacity ) ;
		add ( "fifoDelay" , fifoDelay ) ;
		}
	virtual void validate ()
		{
		if ( busMasterName.size() == 0 )
			ThrowException ( "WritableBusMappedFiFoSettings" , "Bus Master name has zero-length" , __FILE__,__LINE__ ) ;
		if ( fifoName.size() == 0 )
			ThrowException ( "WritableBusMappedFiFoSettings" , "Fifo name has zero-length" , __FILE__,__LINE__ ) ;
		
		if ( burstSize == 0 )
			ThrowException ( "WritableBusMappedFiFoSettings" , "Burst-size is zero" , __FILE__,__LINE__ ) ;
		if ( capacity == 0 )
			ThrowException ( "WritableBusMappedFiFoSettings" , "Capacity is zero" , __FILE__,__LINE__ ) ;
		}
} ;



struct	WritableBusMappedFiFo : public ConfiguredModel<WritableBusMappedFiFoSettings>
{
	SC_HAS_PROCESS(WritableBusMappedFiFo) ;
	
	sc_port<MasterInterface>	mpBus ;
	FiFo::Buffer				mFiFo ;
	Publication<WriteInterface>	mFiFoPublisher ;
	
	Stats::Value<char const*>	mState ;
	Stats::Accumulator<UInt32>	mTotalDataTransferred ;

	WritableBusMappedFiFo	( ModelVariant const & variant )
	
	:	ConfiguredModel<WritableBusMappedFiFoSettings> ( variant )
	,	mpBus ()
	,	mFiFo ( sc_module_name((variant.instanceName+"_fifo").c_str()) , mConfig.capacity , mConfig.fifoDelay )
	,	mFiFoPublisher ( )
	,	mState ( variant.instanceName+".state" , kStateIdle )
	,	mTotalDataTransferred ( variant.instanceName+".totalDataTransferred" , "Words" )
		{
		Log::states("WritableBusMappedFiFo") << identity << " Constructed" ;

		// Publish the FiFo
		mFiFoPublisher	=	mConfig.fifoName ;
		mFiFoPublisher	=	& mFiFo ;
		
		// Connect the bus interface
		Subscription<Bus::MasterInterface>	pWrite	( mConfig.busMasterName ) ;
		bindPortToInterface ( &mpBus , pWrite ) ;
		
		// Make our thread
		SC_THREAD ( writerRunLoop ) ;
		}
	
	void writerRunLoop ()
		{
		while ( 1 )
			{
			mState = kStateIdle ;
			
			if ( mFiFo.availableLength() >= mConfig.burstSize )
				{
				Log::states("WritableBusMappedFiFo") << identity << " We have [enough] available data. Transmitting" ;
				
				Length	count	=	mConfig.burstSize ;
				
				Request	request		( mConfig.targetAddress , kOperationWrite , count ) ;
	
			
				mState = kStateWaitingForBus ;
				mpBus -> request ( Bus::uniqueAddress ( static_cast<void*>(this) ) , request ) ;
				if ( mpBus -> waitForAcknowledge ( Bus::uniqueAddress ( static_cast<void*>(this) ) ) )
					{
					  

					  
					mState = kStateTransferring ;
					
					while ( count -- )
						{
						mpBus -> writeWord ( mFiFo.read() ) ;
						mTotalDataTransferred += 1 ;
					
			
					
						}
					
					mState = kStateIdle ;
					wait ( mFiFo.wasWrittenEvent() ) ;
					}
				else
					{
					mState . setValue ( kStateTransferFailed , Stats::kRetroactive ) ;
					Log::warning("WritableBusMappedFiFo") << "No acknowledgement was received upon write attempt. Will retry later." ;
				
	
				
					}
				}
			else
				{
				mState = kStateIdle ;
				wait ( mFiFo.wasWrittenEvent() ) ;
				}
			}
		}
} ;

TypedModuleMaker<WritableBusMappedFiFo>	gWritableBusMappedFiFoBuilder	( "busMappedFiFo.pushInitiator" ) ;






















struct	ReadableBusMappedFiFoSettings : public Settings
{
	std::string	busSlaveName ;
	std::string	fifoName ;
	unsigned	baseAddress ;
	unsigned	burstSize ;
	unsigned	capacity ;
	SCInterval	fifoDelay ;
	
	ReadableBusMappedFiFoSettings ( std::string const & name )
	:	Settings ( name )
	,	busSlaveName()
	,	fifoName()
	,	baseAddress(0)
	,	burstSize(0)
	,	capacity(0)
	,	fifoDelay(0,kSCIntervalSeconds)
		{
		add ( "busSlaveName" , busSlaveName ) ;
		add ( "fifoName" , fifoName ) ;
		add ( "baseAddress" , baseAddress ) ;
		add ( "addressSpaceSize" , burstSize ) ;
		add ( "capacity" , capacity ) ;
		add ( "fifoDelay" , fifoDelay ) ;
		}
	virtual void validate ()
		{
		if ( busSlaveName.size() == 0 )
			ThrowException ( "ReadableBusMappedFiFoSettings" , "Bus Slave name has zero-length" , __FILE__,__LINE__ ) ;
		if ( fifoName.size() == 0 )
			ThrowException ( "ReadableBusMappedFiFoSettings" , "Fifo name has zero-length" , __FILE__,__LINE__ ) ;
		if ( burstSize == 0 )
			ThrowException ( "ReadableBusMappedFiFoSettings" , "Burst-size is zero" , __FILE__,__LINE__ ) ;
		if ( capacity == 0 )
			ThrowException ( "ReadableBusMappedFiFoSettings" , "Capacity is zero" , __FILE__,__LINE__ ) ;
		}
} ;


struct	ReadableBusMappedFiFo : public ConfiguredModel<ReadableBusMappedFiFoSettings>
{
	SC_HAS_PROCESS(ReadableBusMappedFiFo) ;
	
	sc_port<SlaveInterface>		mpBus ;
	FiFo::Buffer				mFiFo ;
	Publication<WriteInterface>	mFiFoPublisher ;
	
	Stats::Value<char const*>	mState ;
	Stats::Accumulator<UInt32>	mTotalData ;
	
	ReadableBusMappedFiFo	( ModelVariant const & variant )
	:	ConfiguredModel<ReadableBusMappedFiFoSettings> ( variant )
	,	mpBus ()
	,	mFiFo ( sc_module_name((variant.instanceName+"_fifo").c_str()) , mConfig.capacity , mConfig.fifoDelay )
	,	mFiFoPublisher ( )
	,	mState ( variant.instanceName+".state" , kStateIdle )
	,	mTotalData ( variant.instanceName+".totalDataTransferred" , "Words" )
		{
		// Publish the FiFo
		mFiFoPublisher	=	mConfig.fifoName ;
		mFiFoPublisher	=	& mFiFo ;
		
		// Connect the bus interface
		Subscription<Bus::SlaveInterface>	pWrite	( mConfig.busSlaveName ) ;
		bindPortToInterface ( &mpBus , pWrite ) ;
		
		// Make our thread
		SC_THREAD ( readerRunLoop ) ;
		}
	
	void readerRunLoop ()
		{
		while ( 1 )
			{
			mState = kStateIdle ;
			
			mState = kStateWaitingForBus ;
			Request r ;
			mpBus -> waitForNewRequest ( r ) ;
			
			if ( r.operation != kOperationWrite ) continue ;
			
			
			Log::states("ReadableBusMappedFiFo") << identity << " Received request" ;
			
			/** Determine if it's ours **/
			bool	isOurs					( r.baseAddress == mConfig.baseAddress ) ;
			bool	isAppropriatelySmall	( r.blockLength <= mConfig.burstSize ) ;
			bool	willFitInFiFo			( r.blockLength <= mFiFo.availableCapacity() ) ;
			
			if ( isOurs && isAppropriatelySmall && willFitInFiFo )
				{
				mState = kStateTransferring ;
				
				Log::states("ReadableBusMappedFiFo") << identity << " I've got this! I can handle it!" ;
				
				/** Read the data and put it in the fifo **/
				mpBus -> acknowledgeRequest() ;
				
				Length	count	=	r.blockLength ;
				while ( count -- )
					{
					Word	w ;
					mpBus -> readWord ( w ) ;
					mFiFo.write ( w ) ;
					mTotalData += 1 ;
				

				
					}
				}
			else
				{
				Log::states("ReadableBusMappedFiFo") << identity << " - Not for us." ;
				Log::states("ReadableBusMappedFiFo") << "	isOurs = " << isOurs ;
				Log::states("ReadableBusMappedFiFo") << "	isAppropriatelySmall = " << isAppropriatelySmall ;
				Log::states("ReadableBusMappedFiFo") << "	willFitInFiFo = " << willFitInFiFo ;
				}
			
			/** Repeat. Ready, go! **/
			}
		}
} ;

TypedModuleMaker<ReadableBusMappedFiFo>	gReadableBusMappedFiFoBuilder	( "busMappedFiFo.pushAcceptor" ) ;



















